What is downcasting in java?

Downcasting in Java is the process of casting a reference of a superclass to an object of a subclass. It is used when we have a reference to an object of the superclass, but we actually want to treat it as an object of the subclass.

Downcasting is mainly used in situations where we need to access methods or fields that are specific to the subclass. By downcasting, we can access these class-specific features that are not present in the superclass.

However, downcasting can only be done if the object being casted is actually an instance of the subclass. Otherwise, a ClassCastException will be thrown at runtime.

Here's an example:

class Animal {
    public void eat() {
        System.out.println("Animal is eating.");
    }
}

class Dog extends Animal {
    public void eat() {
        System.out.println("Dog is eating.");
    }
    
    public void bark() {
        System.out.println("Dog is barking.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();  // Output: Dog is eating.

        // Downcasting
        Dog dog = (Dog) animal;
        dog.eat();  // Output: Dog is eating.
        dog.bark();  // Output: Dog is barking.
    }
}

In the above example, we create an instance of the Dog class and assign it to a variable of type Animal. We then call the eat() method using the animal reference, which actually calls the overridden method in the Dog class.

To access the bark() method, which is specific to the Dog class, we need to downcast the animal reference to a Dog reference. This allows us to treat the animal object as an instance of Dog and access the bark() method.

It is important to perform type checking before downcasting to avoid ClassCastException. For example, we can use the instanceof operator to check if the object is an instance of the desired subclass:

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.bark();
} else {
    System.out.println("Cannot downcast to Dog.");
}

By performing such type checks, we can safely downcast objects and avoid runtime errors.